
package controller;

import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JLabel;
import javax.swing.JTextField;
import model.test.*;
import view.SetterGUI;
import view.Utilities;

/**
 *
 * @author FELIPE & NICOLAS
 */
public class SetterController {
   
    SetterGUI gui;                                                      // setterGUI object we need in order to modify its components from the controller object.
    
    Test test;                                                          // Test which represents the actual test we are wroking with, it can be a new one (empty), an existing one, or one we are making
    LinkedList<Section> sections = new LinkedList<Section>();           //a linkedList representing the sections of the current Test
    LinkedList<SubSection> subSections = new LinkedList<SubSection>();  //a linkidList reprsenteng the subsections ONLY of the actual selected section
    LinkedList<Question> questions = new LinkedList<Question>();        //a linkedList reprsenting the questions ONLY of the actual selected subsection of the actual selected section
    
    int ActualSection =-1;          //represents the actual selected section in the GUI (0 is the first one). This makes reference to the section position in the LinkedList of Sections
    int ActualSubSection=-1;        //represents the actual selected subSection in the GUI (0 is the first one). This makes reference to the subsection position in the LinkedList of subsections corresponding to the actual section
    int ActualQuestion=-1;          //represents the actual selected question in the GUI (0 is the first one). This makes reference to the question position in the LinkedList of question coresponding to the actual subsection of the actual section
    
    //variables for icons
    final URL urlStop = getClass().getResource("/images/stop64.png");
    final URL urlDone = getClass().getResource("/images/done64.png");
    
    //constructor
    public SetterController(SetterGUI gui){
        this.gui = gui;                         //the constructos receives a SetterGUI object, in order to know which GUI it will modify
    }
       
    public void enableFunctions(){
        gui.btnAddQuestion.setEnabled(true);
        gui.btnAddSection.setEnabled(true);
        gui.btnAddSubSection.setEnabled(true);
        gui.btnDeleteQuestion.setEnabled(true);
        gui.btnDeleteSection.setEnabled(true);
        gui.btnDeleteSubSection.setEnabled(true);
        gui.btnEditQuestion.setEnabled(true);
        gui.btnEditSection.setEnabled(true);
        gui.btnEditSubSection.setEnabled(true);
        gui.btnTakeTest.setEnabled(true);
        gui.btnEditTest.setEnabled(true);
    }
    
    ///////////////////////////////////////////////////////////
    // METHODS FOR EVENTS RELATED TO THE MAIN MENU BOTTONS   //
    ///////////////////////////////////////////////////////////
    
    //method to open a Test file given a filepath (includes file name), and asign it to the test instance variable of this class
    public void openTest(String filepath){
        
        test = OpsFile.readTestFile(filepath);                          //method in the Model which returns Test object     
        gui.lblTestName.setText(test.getName());                        //modify some information labels in GUI
        gui.lblAuthor.setText(test.getAuthor());
        ActualSection=0;                                                //set actual selections to 0 (the first in each)
        ActualSubSection=0;
        ActualQuestion=0;
        sections = test.getSections();                                  //load the sections                                     //en estas 3 lineas esta la clave de q todo funcione. en el controller todo se le hace a las variables Section, subsection, y question, y estas hace referencia directa a los objetos que contiene Test, y cualquier cambio q se haga a ellos se hace implicitamente a Test 
        subSections = sections.get(ActualSection).getSubSections();     //load the subsections of the actual section
        questions = subSections.get(ActualSubSection).getQuestions();   //load the questions of the actual subsection
        this.updateCmbSections();                                       //call this method to update the combo boxes of the GUI
        enableFunctions();
    } 
    
    //method for creating a new test. In this case the test instance variable will be set to a new Test with thi information received from the GUI
    public void createTest(String name, String author, String description){
        
        test = new Test(name,author,description);
        ActualSection =-1;                                              //this variables are set to -1, indicating that there is no actual selection because the Test is now empty
        ActualSubSection=-1;
        ActualQuestion=-1; 
        gui.lblTestName.setText(name);
        gui.lblAuthor.setText(author);
        this.updateCmbSections();                                       //update the combo boxes in the GUI, because they might have information of a previous test
        enableFunctions();
    }
    
 
    
    //method to save the actual Test instance variable, which is represented at the moment in the GUI
    //returns true if the test is complete and can be saved
    public boolean saveTest(){
        
        //Check if the test is incomplete
        //ckeck if there are no empty sections or empty subsections

        LinkedList<SubSection> subSectionsList;
        LinkedList<Question> questionsList;
        boolean emptyTest = false;

        if (!sections.isEmpty()){
         for(Section s:test.getSections()){
                subSectionsList = s.getSubSections();
                if(subSectionsList.isEmpty()){
                        emptyTest = true;
                        break;
                    }
                else{
                    for(SubSection ss:subSectionsList){
                        questionsList = ss.getQuestions();
                        if(questionsList.isEmpty()){
                            emptyTest = true;
                            break;
                        }
                    }
                }
            }
        }
        else
        {
           emptyTest = true;
        }
 
        if(!emptyTest){
           OpsFile.writeTestFile(test, test.getName());//call static method to make a .txt file representing the Test object. The file is saved in a default directory using the name give by the author
           Utilities.messageDialogOK("Test saved!",gui,urlDone);
           return true;
        }
        else{
            Utilities.messageDialogWarning("The actual test is NOT VALID. \n \n Please note the following: \n Every Section must have at least ONE SubSection, and every SubSection at least ONE Question !",gui,urlStop);
            return false;              
        }
    }
    ///////////////////////////////////////////////////////////
    //  METHODS FOR EVENTS RELATED TO CHANGES IN SELECTIONS  //
    ///////////////////////////////////////////////////////////
    
    //method to be called by event listener in the GUI when the selection of the section in the combobox has changed
    public void sectionChanged(int ActualSection){
        
        this.ActualSection=ActualSection;                               //set the ActualSection variable to the one sent by the GUI event                                                         
        subSections = sections.get(ActualSection).getSubSections();
        
        if (!subSections.isEmpty()){
            
            this.ActualSubSection=0; 
            questions = subSections.get(ActualSubSection).getQuestions();

            if (!questions.isEmpty()) {
                this.ActualQuestion=0;
            }
            else {
                this.ActualQuestion=-1;
            }
        }
        else{
            this.ActualSubSection=-1; 
            this.ActualQuestion=-1;
        }

        this.updateCmbSubSections();
        gui.lblSection.setText(sections.get(ActualSection).getName());  
    }
    
    
    //method to be called by event listener in the GUI when the selection of the subsection in the combobox has changed
    public void subSectionChanged(int ActualSubSection){
        
        this.ActualSubSection=ActualSubSection;                         //set the ActualSubSection to the one sent by the GUI event
        
        if (!questions.isEmpty()){     
             this.ActualQuestion=0;                                          //as the questions will be updated with new info, set the selected question to 0
             questions = subSections.get(ActualSubSection).getQuestions();   //update the array of question because subsection changed  
        }
        else {
            this.ActualQuestion=-1;
        }
        this.updateLstQuestions();
        gui.lblSubSection.setText(subSections.get(ActualSubSection).getName());
    }
    
    //method to be called by event listener in the GUI when the selection of the question in the listBox has changed
    public void questionChanged(int ActualQuestion){       
        this.ActualQuestion = ActualQuestion;                           //set the actualQuestion to the one sent by the GUI event
    }
     
    

    ///////////////////////////////////////////////////////////
    //   METHODS FOR EVENTS RELATED WITH DELETE BUTTON       //
    ///////////////////////////////////////////////////////////
    
    public void deleteSection(){
        if(gui.cmbSections.getModel().getSize()!=0){
        Ops.REMOVEsectionFROMtest(sections.get(ActualSection), test);                                       //remove the Section from the List "Sections". Use the ActualSection variable to know which element (position) to remove
        ActualSection=0;                                                                                    //update this variable because when we delete the item now the selected item is 0
        this.updateCmbSections();                                                                           //update combo boxes
        }
    }
    
    public void deleteSubSection(){
        if(gui.cmbSubSections.getModel().getSize()!=0){
        Ops.REMOVEsubSectionFROMsection(subSections.get(ActualSubSection), sections.get(ActualSection));    //remove the SubSection from the List "SubSections" of the actual section. USe the Variables ActualSection and ActualSubSection to know which element to remove from which section
            ActualSubSection=0;                                                                                 //update this variable because when we delete the item now the selected item is 0
        
        this.updateCmbSubSections();  //update combo boxes   
        }
    }
    
    public void deleteQuestion(){
        if(gui.lstQuestions.getModel().getSize() !=0){
        Ops.REMOVEquestionFROMsubSection(questions.get(ActualQuestion),subSections.get(ActualSubSection) );  //remove question from the list of "Questions" of the actual subsection. Use ActualQuestion and Actual SubSection to know which element to remove from which subsection (take into account that the subsection list refers to the list of the actual section
        ActualQuestion=0;                                                                                    //update this variable because when we delete the item now the selected item is 0
        this.updateLstQuestions();  //update lists  
        }
    }  
    

    ///////////////////////////////////////////////////////////
    //     METHODS FOR ADDING OR EDITING QUESTIONS           //
    ///////////////////////////////////////////////////////////
    
    
    //method for saving data typed by user from a question (new or existing)
    public void createEditQuestionMCQ(String questionTxt,int marks,float penalty,ArrayList<String> possAns,int corrAns, String imageFile,boolean newQuestion){
        
        MCQ question = new MCQ(questionTxt,marks,penalty,possAns,corrAns,imageFile);
        
        if (newQuestion){
            
            //if its a new question
  
            Ops.ADDquestionTOsubSection(question,subSections.get(ActualSubSection));
            if (ActualQuestion==-1) {
                ActualQuestion=0;
            }
        }
        else{
            //if its saving an existing question (we can use the Setters, or replace the element in the LinkedList of questions)
            Ops.REPLACEquestionINsubSection(question,subSections.get(ActualSubSection),ActualQuestion);
        }
        this.updateLstQuestions();
    }
    
      //method for saving data typed by user from a question (new or existing)
    public void createEditQuestionFIBQ(String questionTxt,int marks,float penalty,ArrayList<String> corrAns,String imageFile, boolean newQuestion){
        
        FIBQ question = new FIBQ(questionTxt, marks,penalty, corrAns,imageFile);
        
        if (newQuestion){
            
            //if its a new question
  
            Ops.ADDquestionTOsubSection(question,subSections.get(ActualSubSection));
            if (ActualQuestion==-1) {
                ActualQuestion=0;
            }
        }
        else{
            //if its saving an existing question (we can use the Setters, or replace the element in the LinkedList of questions)
            Ops.REPLACEquestionINsubSection(question,subSections.get(ActualSubSection),ActualQuestion);
        }
        
        this.updateLstQuestions();   
    }
    
    public void createEditQuestionMFIBQ(String questionTxt,int marks,float penalty, String imageFile,boolean newQuestion){
        
        MFIBQ question = new MFIBQ(questionTxt, marks,penalty,imageFile);
        
        if (newQuestion){
            
            //if its a new question
  
            Ops.ADDquestionTOsubSection(question,subSections.get(ActualSubSection));
            if (ActualQuestion==-1) {
                ActualQuestion=0;
            }
        }
        else{
            //if its saving an existing question (we can use the Setters, or replace the element in the LinkedList of questions)
            Ops.REPLACEquestionINsubSection(question,subSections.get(ActualSubSection),ActualQuestion);
        }
        
        this.updateLstQuestions();   
    }
    

    //method for retreiving information of actual question to edit it
    //depending on type of question, the corresponding method is called
    public void fillAddEditQuestionDLG(){
        
        Question question = questions.get(ActualQuestion);                      //get actual question Object
        DefaultListModel modelList = new DefaultListModel();                    //for using to fill in list in GUI 
 
        //determine if question is MCQ or FIBQ
        if (question instanceof MCQ) {

            MCQ questionMCQ=(MCQ) question;                                     //cast to MCQ
            this.fillAddEditQuestionDLG_MCQ(questionMCQ);           
        }
        else if (question instanceof FIBQ){
            
            //for FIBQ
             FIBQ questionFIBQ=(FIBQ) question;                                 //cast to FIBQ
             
             this.fillAddEditQuestionDLG_FIBQ(questionFIBQ);
        }
        else if (question instanceof MFIBQ){
             
            //for MFIBQ
             MFIBQ questionMFIBQ=(MFIBQ) question;                                 //cast to MFIBQ
             
             this.fillAddEditQuestionDLG_MFIBQ(questionMFIBQ);
        }
    }
    
    //fill in information of existing question in DLG, called by fillAddEditQuestionDLG()
    private void fillAddEditQuestionDLG_MCQ(MCQ questionMCQ){
            
            DefaultListModel modelList = new DefaultListModel();                    //for using to fill in list in GUI
            
            gui.txtQuestion.setText(questionMCQ.getQuestionTxt());              //write the Question in the GUI
            gui.txtMarks.setText(Integer.toString(questionMCQ.getMarks()));     //write the marks in the GUI
            gui.txtPenalty.setText(Float.toString(questionMCQ.getPenalty()));     //write the penalty in the GUI
            gui.lblImageMCQ.setText(questionMCQ.getImageFile());
            
            for (int i=0;i<questionMCQ.getPossAns().size();i++){                //write all the possible answers int the list (using a model) and combobox
                
                gui.cmbCorrectAnswer.addItem(questionMCQ.getPossAns().get(i));
                modelList.addElement(questionMCQ.getPossAns().get(i));
            }
            
            gui.lstPossAnswers.setModel(modelList);                             //set the model of the list in the GUI with the one just created
            gui.cmbCorrectAnswer.setSelectedIndex(questionMCQ.getCorrAns());    //set the selected index with the actual correct answer
            gui.tabAddEditQuestion.setEnabledAt(1, false);                      //disable the FIBQ tab
            gui.tabAddEditQuestion.setEnabledAt(0,true);                        //enable MCQ
            gui.tabAddEditQuestion.setSelectedIndex(0);
            gui.chbAuto.setSelected(false);
    }
    
    //fill in information of existing question in DLG, called by fillAddEditQuestionDLG()
    private void fillAddEditQuestionDLG_FIBQ(FIBQ questionFIBQ){
            
            DefaultListModel modelList = new DefaultListModel();                    //for using to fill in list in GUI

              String[] tempQuestion = questionFIBQ.getQuestionTxt().split("((?<=<blank>)|(?=<blank>))"); //split the txt question to get the txt areas and blanks 
             //write text question on the dialog box
             for (String temp : tempQuestion){
                 
                 System.out.println(temp);
                 
                 if(temp.equals("<blank>")){
                     gui.pnlQuestion.add(new JLabel("_______"));
                 }
                 else if (!temp.equals("")){
                     JTextField tempText = new JTextField();
                     tempText.setText(temp);
                     gui.pnlQuestion.add(tempText);
                 }
             }

             gui.btnAddBlank.setEnabled(false); //dont allow user tho change question structure
             gui.btnAddText.setEnabled(false);

             gui.tabAddEditQuestion.setSelectedIndex(1);                         //seelct correct tab
             gui.tabAddEditQuestion.setEnabledAt(0, false);                      //disable the MCQ tab
             gui.tabAddEditQuestion.setEnabledAt(1, true);
             gui.tabAddEditQuestion.setSelectedIndex(1);
             
             gui.txtMarksFIB.setText(Integer.toString(questionFIBQ.getMarks()));
             gui.txtPenaltyFIB.setText(Float.toString(questionFIBQ.getPenalty()));
             gui.lblImageBLANK.setText(questionFIBQ.getImageFile());
             
             gui.chbMultipleBlanks.setSelected(false);                          //uncheked because its a SINGLE FIBQ 
             gui.chbMultipleBlanks.setEnabled(false);                           //dont allow user to change it
             
             for (int i=0;i<questionFIBQ.getCorrAns().size();i++){              //write all the possible answers int the list (using a model) and combobox
                
                modelList.addElement(questionFIBQ.getCorrAns().get(i));
             }
               
             gui.lstCorrFIB.setModel(modelList);                                //set the model of the list in the GUI with the one just created
    }
    
    
    //fill in information of existing question in DLG, called by fillAddEditQuestionDLG()
    private void fillAddEditQuestionDLG_MFIBQ(MFIBQ questionMFIBQ){
        
              String[] tempQuestion = questionMFIBQ.getQuestionTxt().split("((?<=<blank>)|(?=<blank>))"); //split the txt question to get the txt areas and blanks 
             //write text question on the dialog box
             for (String temp : tempQuestion){
                 
                 System.out.println(temp);
                 
                 if(temp.equals("<blank>")){
                     gui.pnlQuestion.add(new JLabel("_______"));
                 }
                 else if (!temp.equals("")){
                     JTextField tempText = new JTextField();
                     tempText.setText(temp);
                     gui.pnlQuestion.add(tempText);
                 }
             }
             gui.btnAddBlank.setEnabled(true); //dont allow user tho change question structure
             gui.btnAddText.setEnabled(true);

             gui.tabAddEditQuestion.setSelectedIndex(1);                         //seelct correct tab
             gui.tabAddEditQuestion.setEnabledAt(0, false);                      //disable the MCQ tab
             gui.tabAddEditQuestion.setEnabledAt(1, true);
             gui.tabAddEditQuestion.setSelectedIndex(1);
             
             gui.txtMarksFIB.setText(Integer.toString(questionMFIBQ.getMarks()));
             gui.txtPenaltyFIB.setText(Float.toString(questionMFIBQ.getPenalty()));
             gui.lblImageBLANK.setText(questionMFIBQ.getImageFile());
             
             gui.chbMultipleBlanks.setSelected(true);                          //uncheked because its a SINGLE FIBQ 
             gui.chbMultipleBlanks.setEnabled(false);                           //dont allow user to change it
    }
            
            
    ///////////////////////////////////////////////////////////
    //   METHODS FOR ADDING OR EDITING SUBSECTION (INFO)     //
    ///////////////////////////////////////////////////////////
            
    public void fillAddEditSubSectionDLG(){
        gui.txtSubSectionName.setText(subSections.get(ActualSubSection).getName());
        gui.txtSubSectionDescription.setText(subSections.get(ActualSubSection).getDescTxt());
    }
    
    public void createEditSubSection(String Name,String Description, boolean newSubSection){
        if (newSubSection){
            Ops.ADDsubSectionTOsection(new SubSection(Name, Description), sections.get(ActualSection));
            subSections = sections.get(ActualSection).getSubSections();
            ActualSubSection=subSections.size()-1;
            ActualQuestion=-1;    
        }
        else{
            subSections.get(ActualSubSection).setName(Name);
            subSections.get(ActualSubSection).setDescTxt(Description);
        }
        this.updateCmbSubSections();
        gui.cmbSubSections.setSelectedIndex(ActualSubSection); 
    }
            
    ///////////////////////////////////////////////////////////
    //     METHODS FOR ADDING OR EDITING SECTIONS (INFO)     //
    ///////////////////////////////////////////////////////////
            
            
    public void fillAddEditSectionDLG(){
        gui.txtSectionName.setText(sections.get(ActualSection).getName());
        gui.txtSectionDescription.setText(sections.get(ActualSection).getDescTxt());
        gui.txtSectionTime.setText(Integer.toString(sections.get(ActualSection).getTimeLimit()));
    }
    
    public void createEditSection(String Name,String Description,int Time ,boolean newSection){
        
        if (newSection){
            Section tempSection = new Section(Name, Time, Description);
            Ops.ADDsectionTOtest(tempSection, test);
            
            ActualSection=sections.size()-1;
            ActualSubSection=-1;
            ActualQuestion=-1;   
        }
        else{
            
            sections.get(ActualSection).setName(Name);
            sections.get(ActualSection).setDescTxt(Description);
            sections.get(ActualSection).setTimeLimit(Time);
        }
        
        this.updateCmbSections();
        gui.cmbSections.setSelectedIndex(ActualSection);
    }
    
    ///////////////////////////////////////////////////////////
    //     METHODS FOR ADDING OR EDITING TEST (INFO)         //
    ///////////////////////////////////////////////////////////
    
    public void editTest(String name, String author,String description){
        test.setName(name);
        test.setAuthor(author);
        test.setDescTxt(description);
        gui.lblTestName.setText(name);
        gui.lblAuthor.setText(author);
    }
    
    public void fillEditTestDLG(){
        gui.txtEditTestName.setText(test.getName());
        gui.txtEditAuthor.setText(test.getAuthor());
        gui.txtEditTestDesc.setText(test.getDescTxt());
    }
    
    
    ///////////////////////////////////////////////////////////
    //    INNER METHODS FOR UPDATING COMBOXES AND LIST       //
    ///////////////////////////////////////////////////////////
    
    public void updateCmbSections(){
        
        sections = test.getSections();  //get the sections
        
        if(sections.size()!=0){
            
            DefaultComboBoxModel modelSections = new DefaultComboBoxModel();
            for (Section i : sections)
                modelSections.addElement(i.getName());
            gui.cmbSections.setModel(modelSections);
            gui.lblSection.setText(sections.get(ActualSection).getName());  
         }
        else
        {
            gui.cmbSections.setModel(new DefaultComboBoxModel());
            gui.cmbSubSections.setModel(new DefaultComboBoxModel());            //write a clear model
            gui.lstQuestions.setModel(new DefaultListModel());
            gui.lblSection.setText("");
            gui.lblSubSection.setText("");
        }
        this.updateCmbSubSections();
    }

    
    ///////////////
    public void updateCmbSubSections() {
 
        sections = test.getSections();  //get the sections
        
        if (sections.size()!=0){
            
            subSections = sections.get(ActualSection).getSubSections();      //get subsections in the section
            
            if (subSections.size() != 0) {

                DefaultComboBoxModel modelSubSections = new DefaultComboBoxModel();
                gui.lblSection.setText(gui.cmbSections.getSelectedItem().toString());
                subSections = sections.get(ActualSection).getSubSections();
                for (SubSection i : subSections) {
                    modelSubSections.addElement(i.getName());
                }
                gui.cmbSubSections.setModel(modelSubSections);
                gui.lblSubSection.setText(gui.cmbSubSections.getSelectedItem().toString());
            }
            else {
                //if there are no subsections show empty combobox
                gui.cmbSubSections.setModel(new DefaultComboBoxModel());            //write a clear model
                gui.lstQuestions.setModel(new DefaultListModel());
                gui.lblSubSection.setText("");
            }  
        }
        else{
            //if there is no sections for sure there is no subsection
            gui.cmbSubSections.setModel(new DefaultComboBoxModel());            //write a clear model
            gui.lstQuestions.setModel(new DefaultListModel());
            gui.lblSubSection.setText("");
        }
        
        this.updateLstQuestions();
    }
    
    
    ///////////////////
    public void updateLstQuestions(){
        
        sections = test.getSections();  //get the sections
        
        if (sections.size()!=0){
            
            subSections = sections.get(ActualSection).getSubSections();      //get subsections in the section
            
            if(subSections.size() != 0) {
                
                questions = subSections.get(ActualSubSection).getQuestions();   //get the questions in the subsection
                
                if (questions.size() != 0) {
                    gui.lblSubSection.setText(gui.cmbSubSections.getSelectedItem().toString());
                    DefaultListModel modelQuestions = new DefaultListModel();
                    subSections = sections.get(ActualSection).getSubSections();
                    questions = subSections.get(ActualSubSection).getQuestions();
                    for (Question i : questions) {
                        
                        //write the question text, ading type of question and replacing <blank> tag
                        modelQuestions.addElement(i.getType()+" : "+Utilities.replace(i.getQuestionTxt(),"<blank>"," ________ "));
                    }
                    gui.lstQuestions.setModel(modelQuestions);
                } 
                else {
                    gui.lstQuestions.setModel(new DefaultListModel());
                    
                }    
            }
            else{
                //if no subsections exist for sure there are no questions
                gui.lstQuestions.setModel(new DefaultListModel());
            } 
        }
        else{
            //if no sections exist for sure there are no questions
            gui.lstQuestions.setModel(new DefaultListModel());
        }
    }
    
    public Test getTest(){
        return test;
    }  
}
